From 69c16fc6c8cec98e56bb0b25d3d38b2d0fa2d97a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 18 Jul 2014 08:40:45 -0700 Subject: [PATCH] Implement cargo-package This command will assemble the current package into a tarball ready for uploading to the cargo registry. Currently no further verification is done beyond packaging the local repository into a tarball, but in the future this could execute other operations such as api stability tools. --- Cargo.lock | 12 +++++++ Cargo.toml | 6 ++++ src/bin/cargo.rs | 1 + src/bin/package.rs | 31 ++++++++++++++++++ src/cargo/core/package_id.rs | 2 +- src/cargo/lib.rs | 8 ++--- src/cargo/ops/cargo_package.rs | 60 ++++++++++++++++++++++++++++++++++ src/cargo/ops/mod.rs | 2 ++ tests/support/mod.rs | 1 + tests/test_cargo_package.rs | 27 +++++++++++++++ tests/tests.rs | 1 + 11 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 src/bin/package.rs create mode 100644 src/cargo/ops/cargo_package.rs create mode 100644 tests/test_cargo_package.rs diff --git a/Cargo.lock b/Cargo.lock index 6b02ba227..4f5ea5f24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,8 +4,10 @@ version = "0.0.1-pre" dependencies = [ "docopt 0.6.0 (git+https://github.com/burntsushi/docopt.rs#fc7ba2f1a5a351f7874257d880223d2ff5c75d36)", "docopt_macros 0.6.0 (git+https://github.com/burntsushi/docopt.rs#fc7ba2f1a5a351f7874257d880223d2ff5c75d36)", + "flate2 0.0.1 (git+https://github.com/alexcrichton/flate2-rs#12593d1b9ccf09c2eabac176a6e233b171eed843)", "hamcrest 0.1.0 (git+https://github.com/carllerche/hamcrest-rust.git#f0fd1546b0a7a278a12658ab8602b5c827cc3a42)", "semver 0.0.1 (git+https://github.com/rust-lang/semver#c78b40d7fdf8acd99b503e6ce394fbcf9eb8982f)", + "tar 0.0.1 (git+https://github.com/alexcrichton/tar-rs#689bbc003ae47feae5bc99c53b56736e4ad994ba)", "toml 0.1.0 (git+https://github.com/alexcrichton/toml-rs#8c128cb550ba66d4962cd81acca93857c605eaa0)", "url 0.1.0 (git+https://github.com/servo/rust-url#23fb9ec22cca9d643ad2cce9894a947c005b7fe2)", ] @@ -28,6 +30,11 @@ name = "encoding" version = "0.1.0" source = "git+https://github.com/lifthrasiir/rust-encoding#7e7950ddbd46428a439db3e2594fa78f0972ef2e" +[[package]] +name = "flate2" +version = "0.0.1" +source = "git+https://github.com/alexcrichton/flate2-rs#12593d1b9ccf09c2eabac176a6e233b171eed843" + [[package]] name = "hamcrest" version = "0.1.0" @@ -38,6 +45,11 @@ name = "semver" version = "0.0.1" source = "git+https://github.com/rust-lang/semver#c78b40d7fdf8acd99b503e6ce394fbcf9eb8982f" +[[package]] +name = "tar" +version = "0.0.1" +source = "git+https://github.com/alexcrichton/tar-rs#689bbc003ae47feae5bc99c53b56736e4ad994ba" + [[package]] name = "toml" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 4df2ec39c..e393cb5c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,12 @@ git = "https://github.com/servo/rust-url" [dependencies.semver] git = "https://github.com/rust-lang/semver" +[dependencies.tar] +git = "https://github.com/alexcrichton/tar-rs" + +[dependencies.flate2] +git = "https://github.com/alexcrichton/flate2-rs" + [[bin]] name = "cargo" test = false diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index d92837e40..140f2d26d 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -59,6 +59,7 @@ macro_rules! each_subcommand( ($macro:ident) => ({ $macro!(git_checkout) $macro!(locate_project) $macro!(new) + $macro!(package) $macro!(read_manifest) $macro!(run) $macro!(test) diff --git a/src/bin/package.rs b/src/bin/package.rs new file mode 100644 index 000000000..18af63866 --- /dev/null +++ b/src/bin/package.rs @@ -0,0 +1,31 @@ +use docopt; +use cargo::ops; +use cargo::core::{MultiShell}; +use cargo::util::{CliResult, CliError}; +use cargo::util::important_paths::find_root_manifest_for_cwd; + +docopt!(Options, " +Assemble a the local package into a distributable tarball + +Usage: + cargo package [options] + +Options: + -h, --help Print this message + --manifest-path PATH Path to the manifest to compile + -v, --verbose Use verbose output + +", flag_manifest_path: Option) + +pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult> { + shell.set_verbose(options.flag_verbose); + let Options { + flag_manifest_path, + .. + } = options; + + let root = try!(find_root_manifest_for_cwd(flag_manifest_path.clone())); + ops::package(&root, shell).map(|_| None).map_err(|err| { + CliError::from_boxed(err, 101) + }) +} diff --git a/src/cargo/core/package_id.rs b/src/cargo/core/package_id.rs index 6f70ad353..8070d7c3a 100644 --- a/src/cargo/core/package_id.rs +++ b/src/cargo/core/package_id.rs @@ -1,7 +1,7 @@ use semver; use std::hash::Hash; use std::fmt::{mod, Show, Formatter}; -use collections::hash; +use std::hash; use serialize::{ Encodable, Encoder, diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs index db4e6912e..ff95e2d87 100644 --- a/src/cargo/lib.rs +++ b/src/cargo/lib.rs @@ -5,19 +5,19 @@ #![feature(default_type_params)] #![deny(warnings)] -extern crate collections; -extern crate debug; extern crate glob; extern crate regex; -extern crate semver; extern crate serialize; extern crate term; extern crate time; -extern crate url; #[phase(plugin)] extern crate regex_macros; #[phase(plugin, link)] extern crate log; +extern crate semver; extern crate docopt; +extern crate flate2; +extern crate tar; +extern crate url; extern crate toml; #[cfg(test)] extern crate hamcrest; diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs new file mode 100644 index 000000000..b5c718793 --- /dev/null +++ b/src/cargo/ops/cargo_package.rs @@ -0,0 +1,60 @@ +use std::io::File; + +use tar::Archive; +use flate2::{GzBuilder, BestCompression}; + +use core::source::Source; +use core::{Package, MultiShell}; +use sources::PathSource; +use util::{CargoResult, human, internal, ChainError, Require}; + +pub fn package(manifest_path: &Path, + shell: &mut MultiShell) -> CargoResult { + let mut src = try!(PathSource::for_path(&manifest_path.dir_path())); + try!(src.update()); + let pkg = try!(src.get_root_package()); + + let filename = format!("{}-{}.tar.gz", pkg.get_name(), pkg.get_version()); + let dst = pkg.get_manifest_path().dir_path().join(filename); + try!(shell.status("Packaging", pkg.get_package_id().to_string())); + try!(tar(&pkg, &src, shell, &dst).chain_error(|| { + human("failed to prepare local package for uploading") + })); + + Ok(dst) +} + +fn tar(pkg: &Package, src: &PathSource, shell: &mut MultiShell, + dst: &Path) -> CargoResult<()> { + + if dst.exists() { + return Err(human(format!("destination already exists: {}", + dst.display()))) + } + let tmpfile = try!(File::create(dst)); + + // Prepare the encoder and its header + let encoder = GzBuilder::new().filename(dst.filename().unwrap()) + .writer(tmpfile, BestCompression); + + // Put all package files into a compressed archive + let ar = Archive::new(encoder); + for file in try!(src.list_files(pkg)).iter() { + let relative = file.path_relative_from(&dst.dir_path()).unwrap(); + let relative = try!(relative.as_str().require(|| { + human(format!("non-utf8 path in source directory: {}", + relative.display())) + })); + let mut file = try!(File::open(file)); + try!(shell.verbose(|shell| { + shell.status("Archiving", relative.as_slice()) + })); + let path = format!("{}-{}/{}", pkg.get_name(), + pkg.get_version(), relative); + try!(ar.append(path.as_slice(), &mut file).chain_error(|| { + internal(format!("could not archive source file `{}`", relative)) + })); + } + + Ok(()) +} diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index 71846ecf6..3635857c7 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -8,6 +8,7 @@ pub use self::cargo_doc::{doc, DocOptions}; pub use self::cargo_generate_lockfile::{generate_lockfile, write_resolve}; pub use self::cargo_generate_lockfile::{update_lockfile, load_lockfile}; pub use self::cargo_test::{run_tests, run_benches, TestOptions}; +pub use self::cargo_package::package; mod cargo_clean; mod cargo_compile; @@ -18,3 +19,4 @@ mod cargo_new; mod cargo_doc; mod cargo_generate_lockfile; mod cargo_test; +mod cargo_package; diff --git a/tests/support/mod.rs b/tests/support/mod.rs index c480b6f4a..08e42c935 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -507,3 +507,4 @@ pub static COMPILING: &'static str = " Compiling"; pub static FRESH: &'static str = " Fresh"; pub static UPDATING: &'static str = " Updating"; pub static DOCTEST: &'static str = " Doc-tests"; +pub static PACKAGING: &'static str = " Packaging"; diff --git a/tests/test_cargo_package.rs b/tests/test_cargo_package.rs new file mode 100644 index 000000000..969a2e647 --- /dev/null +++ b/tests/test_cargo_package.rs @@ -0,0 +1,27 @@ +use support::{project, execs}; +use support::{PACKAGING}; +use hamcrest::{assert_that, existing_file}; + +fn setup() { +} + +test!(simple { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() { println!("hello"); } + "#); + + assert_that(p.cargo_process("package"), + execs().with_status(0).with_stdout(format!("\ +{packaging} foo v0.0.1 ({dir}) +", + packaging = PACKAGING, + dir = p.url()).as_slice())); + assert_that(&p.root().join("foo-0.0.1.tar.gz"), existing_file()); +}) diff --git a/tests/tests.rs b/tests/tests.rs index 966e572aa..c84e20633 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -37,3 +37,4 @@ mod test_cargo_compile_plugins; mod test_cargo_doc; mod test_cargo_freshness; mod test_cargo_generate_lockfile; +mod test_cargo_package; -- 2.30.2